package com.stars.easyms.feign.request;

import feign.Request;
import feign.RequestTemplate;
import feign.codec.Encoder;
import feign.form.ContentType;
import feign.form.spring.SpringFormEncoder;
import feign.form.util.PojoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

import static feign.form.ContentProcessor.CONTENT_TYPE_HEADER;

/**
 * 为feign增加request解析
 *
 * @author guoguifang
 * @date 2020-08-17 12:34
 * @since 1.6.1
 */
@Slf4j
@SuppressWarnings("unchecked")
public class EasyMsFeignEncoder extends FeignRequestInterceptor implements Encoder {

    private final Encoder encoder;

    private final EasyMsGetFeignEncoder easyMsGetFeignEncoder;

    private final SpringFormEncoder springFormEncoder;

    public EasyMsFeignEncoder(Encoder encoder) {
        super();
        this.encoder = encoder;
        this.easyMsGetFeignEncoder = new EasyMsGetFeignEncoder();
        this.springFormEncoder = new SpringFormEncoder(encoder);
    }

    @Override
    public void encode(Object object, Type bodyType, RequestTemplate requestTemplate) {

        // 获取contentType的值
        String contentTypeValue = getContentTypeValue(requestTemplate.headers());

        // 先判断是否是上传模式,如果是则只记录文件名称
        List<String> multipartTraceObject = getMultipartTraceObject(object, bodyType);
        if (multipartTraceObject != null) {

            // 检查content-type的值是否正确
            checkMultipartContentType(contentTypeValue, requestTemplate);

            // 记录上传文件名称
            traceRequest(requestTemplate, multipartTraceObject, ContentType.MULTIPART.getHeader());

            // 使用springFormEncoder进行编码
            springFormEncoder.encode(object, bodyType, requestTemplate);
            return;
        }

        // 如果是MULTIPART模式并且bodyType为MAP_STRING_WILDCARD的话则使用springFormEncoder
        ContentType contentType = ContentType.of(contentTypeValue);
        if (ContentType.MULTIPART == contentType && MAP_STRING_WILDCARD.equals(bodyType)) {
            Map<String, Object> data = (Map<String, Object>) object;
            multipartTraceObject = data.values().stream()
                    .filter(s -> s instanceof MultipartFile)
                    .map(s -> ((MultipartFile) s).getOriginalFilename())
                    .collect(Collectors.toList());

            // 记录上传文件名称
            traceRequest(requestTemplate, multipartTraceObject, contentTypeValue);

            // 使用springFormEncoder进行编码
            springFormEncoder.encode(object, bodyType, requestTemplate);
            return;
        }

        // 如果是urlencoded模式的话则使用springFormEncoder
        if (ContentType.URLENCODED == contentType && object != null && PojoUtil.isUserPojo(bodyType)) {

            // 记录form表单信息
            traceRequest(requestTemplate, PojoUtil.toMap(object), contentTypeValue);

            // 使用springFormEncoder进行编码
            springFormEncoder.encode(object, bodyType, requestTemplate);
            return;
        }

        // 如果content-type为空则默认使用application/json的media
        if (StringUtils.isBlank(contentTypeValue)) {
            contentTypeValue = MediaType.APPLICATION_JSON_VALUE;
            requestTemplate.header(CONTENT_TYPE_HEADER, contentTypeValue);
        }
        traceRequest(requestTemplate, object, contentTypeValue);

        // 判断是否是get模式，如果是get模式并且有requestBody参数的时候进行拼装
        if (object != null && Request.HttpMethod.GET.name().equalsIgnoreCase(requestTemplate.method())) {
            easyMsGetFeignEncoder.encode(object, bodyType, requestTemplate);
        } else {
            encoder.encode(object, bodyType, requestTemplate);
        }
    }

    private List<String> getMultipartTraceObject(Object object, Type bodyType) {
        List<String> fileNameList = new ArrayList<>();
        if (bodyType.equals(MultipartFile[].class)) {
            MultipartFile[] files = (MultipartFile[]) object;
            for (MultipartFile file : files) {
                fileNameList.add(file.getOriginalFilename());
            }
        } else if (bodyType.equals(MultipartFile.class)) {
            MultipartFile file = (MultipartFile) object;
            fileNameList.add(file.getOriginalFilename());
        } else if (isMultipartFileCollection(object)) {
            Iterable<?> iterable = (Iterable<?>) object;
            for (Object item : iterable) {
                MultipartFile file = (MultipartFile) item;
                fileNameList.add(file.getOriginalFilename());
            }
        }
        return fileNameList.isEmpty() ? null : fileNameList;
    }

    private boolean isMultipartFileCollection(Object object) {
        if (!(object instanceof Iterable)) {
            return false;
        }
        Iterator iterator = ((Iterable<?>) object).iterator();
        return iterator.hasNext() && iterator.next() instanceof MultipartFile;
    }

    private void checkMultipartContentType(String contentTypeValue, RequestTemplate requestTemplate) {
        // 如果是上传模式则对Content-Type校验是否正确,若不正确则输入正确的值,先重置再置入
        if (contentTypeValue == null) {
            requestTemplate.header(CONTENT_TYPE_HEADER, ContentType.MULTIPART.getHeader());
            return;
        }

        // 如果正确则直接返回
        ContentType contentType = ContentType.of(contentTypeValue);
        if (ContentType.MULTIPART == contentType) {
            return;
        }

        // 如果包含其他参数则把其他参数同步增加
        int semicolonIndex = contentTypeValue.indexOf(";");
        contentTypeValue = semicolonIndex < 0 ?
                ContentType.MULTIPART.getHeader() :
                ContentType.MULTIPART.getHeader() + contentTypeValue.substring(semicolonIndex);
        requestTemplate.header(CONTENT_TYPE_HEADER, Collections.emptyList());
        requestTemplate.header(CONTENT_TYPE_HEADER, contentTypeValue);
    }

}
